package sbc.group1.lab1.alt;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.HashSet;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import sbc.group1.lab1.common.Assembler;
import sbc.group1.lab1.common.Car;
import sbc.group1.lab1.common.CarBody;
import sbc.group1.lab1.common.Engine;
import sbc.group1.lab1.common.Order;
import sbc.group1.lab1.common.Wheel;
import sun.misc.Signal;
import sun.misc.SignalHandler;

class AssemblerImpl implements Runnable, Assembler{
    
    public static final Logger log = LoggerFactory.getLogger(AssemblerImpl.class);

    
    public static void main(String[] args) throws URISyntaxException, NumberFormatException, NotBoundException, IOException, InterruptedException {
        final AssemblerImpl monteur= new AssemblerImpl(
                Integer.parseInt(args[0]));
        final Thread t = new Thread(monteur, "assembler");
        Signal.handle(new Signal("INT"), 
                new SignalHandler() {
                    @Override
                    public void handle(Signal sig) {
                        t.interrupt();
                    }
                });
            
        if (System.getProperty("benchmark") != null && System.getProperty("benchmark").equals("true")) {
            Signal.handle(new Signal("HUP"), 
                    new SignalHandler() {
                        @Override
                        public void handle(Signal sig) {
                            t.start();
                        }
                    });
            System.in.read();
        }else {
            t.start();
            System.in.read();
            t.interrupt();
            t.join();
        }
    }

    private CarService cs;
    private volatile boolean running;
    private final int workerId;
    
    public AssemblerImpl(int workerId) throws RemoteException, MalformedURLException, NotBoundException {
    	cs= (CarService)
                Naming.lookup("rmi://localhost:1099/CarService");
        //cs.init(configFile, spaceURI);
        this.workerId=workerId;
    }
    
    
    @Override
    public void run() {
        log.info("Assembler started...");
        running=true;
        while(running) {
            try {
                produce();
                log.debug("Car successfully assembled.");
            } catch (RemoteException e1) {
                log.error("Car assembly failed!",e1);
            } catch (InterruptedException e) {
                log.info("Interrupted! Stopping...");
                running=false;
                return;
            }
        }
    }
    
    private void produce() throws RemoteException, InterruptedException { 
    	Engine e = null;
    	CarBody cb = null;
    	boolean hasDone = false;
    	for(Order o : cs.ReadOrders()){
    		
    		AssemblyJob j = cs.getAssemblyJob(o);
    		if(j != null){
    			e = cs.TakeEngine(o.getEngine());
    			if(e != null){
    				CoaterJob cjob = cs.getCoaterJob(o);
    				if(cjob == null){
    					cb = cs.TakeCarBody(o);
    					if(cb == null){
    						cs.putCoaterJob(cjob, o);
    						cjob=null;
    					}
    				}
    				if (cb==null) {
    					cb = cs.takeUncoatedCarBody();
    				}
    				if(cb != null){
    					boolean success = produce(e, cb, o);
    					if(!success){
    						cs.addEngine(e);
    						cs.addCarBody(cb);
    						cs.putAssemblyJob(j, o);
    						if (cjob != null){
    							cs.putCoaterJob(cjob, o);
    						}
    					}
    					else{
    						hasDone = true;
    						break;
    					}
    				}
    				else{
    					cs.addEngine(e);
    					cs.putAssemblyJob(j, o);
						if (cjob != null){
							cs.putCoaterJob(cjob, o);
						}
    				}
    			}   			
    		}
    	}
    	if(!hasDone){
    		produce(null, null, null);
    	}
              
    }
    
    private boolean produce(Engine engine, CarBody body, Order o) throws RemoteException, InterruptedException{
    	if(engine==null){
    		engine = cs.TakeEngine();   
    	}
        Wheel w1 = cs.TakeWheel();
        Wheel w2 = cs.TakeWheel();
        Wheel w3 = cs.TakeWheel();
        Wheel w4 = cs.TakeWheel();
        HashSet<Wheel> wheels = new HashSet<Wheel>();
        wheels.add(w1);
        wheels.add(w2);
        wheels.add(w3);
        wheels.add(w4);
        if(body == null){
        	body = cs.TakeCarBody();
        }
        
        Car newAuto = null;
        if(o == null){
        	newAuto = new Car(body, engine, wheels, this);   
        }
        else{
        	newAuto = new Car(body, engine, wheels, this, o);
        }
        
        cs.addCar(newAuto);
        return true;
    }


    @Override
    public int getId() {
        return workerId;
    }
}
